﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using XYZShell.CSHelper.Data;
using System.Reflection;
using System.Threading.Tasks;

namespace DotNetEnumTools
{
    /// <summary>
    /// 同步 .net中枚举信息到db方便查询
    /// </summary>
    public class EnumSyncDb
    {
        private string path;
        private string dbfile;
        private Queue<Assembly> dotnetAssemblyList = new Queue<Assembly>();
        private Queue<EnumInfo> enumInfos = new Queue<EnumInfo>();
        bool isEnd = false;
        bool isEndEnum = false;
        public EnumSyncDb(string path, string dbfile)
        {
            this.path = path;
            this.dbfile = dbfile;
        }
        SqliteHelper db;

        private long insertEnum(string name, string ns, string rm)
        {
            string sql = "insert into Enum_Info (Enum_Name,Enum_Namespace,Enum_Remark) values (@name,@namespace,@remark);select last_insert_rowid() from Enum_Info;";
            var pars = new StatementParameterCollection();
            pars.AddInParameter("@name", System.Data.DbType.String, name);
            pars.AddInParameter("@namespace", System.Data.DbType.String, ns);
            pars.AddInParameter("@remark", System.Data.DbType.String, rm);
            return (long)db.ExecuteScalar(sql, pars);
        }
        private void insertEnumItem(long id, string name, string value, string rm)
        {
            string sql = "insert into Enum_Item_Info (Enum_ID,Enum_Item_Name,Enum_Item_Value,Enum_Item_Remark) values (@id, @name,@value, @remark);";

            var pars = new StatementParameterCollection();
            pars.AddInParameter("@id", System.Data.DbType.Int64, id);
            pars.AddInParameter("@name", System.Data.DbType.String, name);
            pars.AddInParameter("@value", System.Data.DbType.String, value);
            pars.AddInParameter("@remark", System.Data.DbType.String, rm);
            db.ExecuteNonQuery(sql, pars);
        }
        private void init()
        {

            Console.WriteLine("初始化");
            Console.WriteLine("检查路径{0}", path);
            if (!Directory.Exists(path))
            {
                throw new Exception("路径不存在");
            }
            else
            {
                Console.WriteLine("路径存在");
            }
            Console.WriteLine("检查数据文件{0}", dbfile);
            db = new SqliteHelper(string.Format("Data Source= {0}", dbfile));
            if (File.Exists(dbfile))
            {
                Console.WriteLine("文件存在");
            }
            else
            {
                Console.WriteLine("文件不存在，初始化数据文件");
                SqliteHelper.Create(dbfile);
                string sql = @"CREATE TABLE [Enum_Info] (
	[ID] integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
	[Enum_Name] nvarchar(254), 
    [Enum_Namespace] nvarchar(254),
	[Enum_Remark] text
);

CREATE TABLE [Enum_Item_Info] (
	[ID] integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
	[Enum_ID] integer, 
	[Enum_Item_Name] nvarchar(254),
[Enum_Item_Value] nvarchar(254),
[Enum_Item_Remark] text);

";
                db.ExecuteNonQuery(sql);
                Console.WriteLine("初始化完成");
            }


        }

        private void syncdll()
        {
            while (true)
            {
                if (isEnd && dotnetAssemblyList.Count <= 0)
                {
                    break;
                }
                if (dotnetAssemblyList.Count == 0)
                {
                    continue;
                }
                var ass = dotnetAssemblyList.Dequeue();
                scandll(ass);
            }
            isEndEnum = true;
        }
        private void scandll(Assembly ass)
        {
            try
            {
                Console.WriteLine("scan {0} Assembly types", ass.FullName);
                var types = ass.GetTypes();
                foreach (var item in types)
                {
                    if (!item.IsEnum)
                    {
                        continue;
                    }
                    var values = item.GetEnumValues();
                    //long id = insertEnum(item.Name, item.Namespace, "");
                    var ii = item.GetEnumUnderlyingType();
                    var enuminfo = new EnumInfo();
                    enuminfo.Name = item.Name;
                    enuminfo.EnumNamespace = item.Namespace;
                    foreach (var enumitem in values)
                    {
                        var name = enumitem.ToString();
                        string vl = name;
                        if (ii == typeof(Int32))
                        {
                            var v = (int)enumitem;
                            vl = v.ToString();
                            //insertEnumItem(id, name, v.ToString(), "");
                        }
                        enuminfo.Items.Add(new EnumInfo.EnumItemInfo() { Name = name, Value = vl });
                    }
                    enumInfos.Enqueue(enuminfo);
                }
                Console.WriteLine("ok");
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.ToString());
            }

        }
        private void syncdb()
        {
            int count = 0;
            db.Begin();
            while (true)
            {
                if (isEndEnum && enumInfos.Count <= 0)
                {
                    break;
                }
                if (enumInfos.Count == 0)
                {
                    continue;
                }
                var enuminfo = enumInfos.Dequeue();
                enuminfo.ID = insertEnum(enuminfo.Name, enuminfo.EnumNamespace, "");
                foreach (var item in enuminfo.Items)
                {
                    insertEnumItem(enuminfo.ID, item.Name, item.Value, "");
                }
                if (count > 500)
                {
                    db.Commit();
                    Console.WriteLine("sync db 500+ lines");
                    db.Begin();
                    count = 0;
                }
                count++;
            }
            db.Commit();
        }

        private void scanfile(string path)
        {
            var dlls = Directory.GetFiles(path, "*.dll");
            foreach (var item in dlls)
            {
                try
                {
                    Console.WriteLine("loading {0}", item);                    
                    var ass = Assembly.LoadFile(item);                   
                    Console.WriteLine("ok");
                    scandll(ass);
                    //dotnetAssemblyList.Enqueue(ass);
                }
                catch (Exception ex)
                {

                    Console.WriteLine(ex.ToString());
                }

            }
            var dirs = Directory.GetDirectories(path);
            foreach (var item in dirs)
            {
                scanfile(item);
            }
        }
        public void Run()
        {
            try
            {
                init();
                scanfile(path);
                isEnd = true;

                Task.Factory.StartNew(() =>
                {
                    scanfile(path);
                    isEnd = true;
                    isEndEnum = true;
                });
                Task.Factory.StartNew(syncdb);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

        }
    }

    public class EnumInfo
    {
        public EnumInfo()
        {
            this.Items = new List<EnumItemInfo>();
        }
        public long ID { get; set; }
        public string Name { get; set; }
        public string EnumNamespace { get; set; }
        public string Remark { get; set; }

        public List<EnumItemInfo> Items { get; set; }

        public class EnumItemInfo
        {
            public string Name { get; set; }
            public string Value { get; set; }
            public string Remark { get; set; }
        }
    }
}
